package com.dragon.english_practice_back.service.impl;

import cn.hutool.json.JSONUtil;
import com.dragon.english_practice_back.entity.WordBook;
import com.dragon.english_practice_back.enums.RedisPrefixEnum;
import com.dragon.english_practice_back.mapper.WordsMapper;
import com.dragon.english_practice_back.service.AsyncRedisUpdateService;
import com.dragon.english_practice_back.service.WordBookService;
import com.dragon.english_practice_back.service.WordScheduleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@Slf4j
@EnableScheduling
@EnableAsync
public class WordScheduleServiceImpl implements WordScheduleService {
    @Value("${app.schedule.words:20}")
    private int scheduleWords;
    RedisTemplate<String, Map<String, Object>> redisTemplate;
    WordBookService wordBookService;
    WordsMapper wordsMapper;
    AsyncRedisUpdateService asyncRedisUpdateService;

    public WordScheduleServiceImpl(RedisTemplate<String, Map<String, Object>> redisTemplate,
                                  WordBookService wordBookService,
                                  WordsMapper wordsMapper,
                                   AsyncRedisUpdateService asyncRedisUpdateService) {
        this.redisTemplate = redisTemplate;
        this.wordBookService = wordBookService;
        this.wordsMapper = wordsMapper;
        this.asyncRedisUpdateService = asyncRedisUpdateService;
    }

    @Override
    public List<Map<String, Object>> getTodayWordSchedule(int userId) {
        // 获取所有单词
        return getWordsFromSchedule(userId, -1);
    }

    @Override
    public List<Map<String, Object>> getWordsFromSchedule(int userId, int num) {
        // 获取当前用户正在学习的单词书id
        Integer bookId = getLearningWordBookId(userId);

        // 检查redis中是否有该用户的单词计划
        long redisListSize = redisTemplate.opsForList().size(
                RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix() + userId
                        + RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK.getPrefix() + bookId);

        // 没有则从数据库中获取
        if (redisListSize == 0){
            log.info("从数据库中获取单词计划");
            List<Map<String, Object>> generatedWordSchedule = generateWordSchedule(userId, bookId);

            // 检查generatedWordSchedule是否为空
            if (generatedWordSchedule == null || generatedWordSchedule.isEmpty()){
                return null;
            }
            // 将currentWords存入redis中
            redisTemplate.opsForList().rightPushAll(
                    RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix() + userId
                            + RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK.getPrefix() + bookId,
                    generatedWordSchedule);
        }

        // 从redis中获取单词计划
        log.info("从redis中获取单词计划");
        // 检查num是否大于0或者num是不是大于redisListSize了,如果不是则返回所有单词
        if (num<=0 || num>=redisListSize){
            return redisTemplate.opsForList().range(
                    RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix() + userId
                            + RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK.getPrefix() + bookId,
                    0, redisListSize - 1);
        }
        List<Integer> index = new ArrayList<>();
        for (int i = 0; i < redisListSize; i++) {
            index.add(i);
        }
        List<Integer> randomIndexList = randomSample(index, num);
        List<Map<String, Object>> wordSchedule = new ArrayList<>();
        randomIndexList.forEach(randomIndex -> {
            wordSchedule.add(redisTemplate.opsForList().index(
                    RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix() + userId
                            + RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK.getPrefix() + bookId,
                    randomIndex));
        });

        return wordSchedule;
    }

    @Override
    @Async
    public void updateWordProficiency(int userId, List<Integer> wordIds, Boolean isCorrect) {
        if (wordIds == null || wordIds.isEmpty()){
            return ;
        }
        // 遍历出这些id在redis中的下标
        List<Integer> index = new ArrayList<>();
        List<Map<String, Object>> todayWordSchedule = getTodayWordSchedule(userId);
        for (int i = 0; i < todayWordSchedule.size(); i++) {
            if (wordIds.contains((Integer) todayWordSchedule.get(i).get("id"))){
                index.add(i);
            }
        }
        // 检查index是否为空
        if (index.isEmpty()){
            return ;
        }

        // 使用多线程更新redis中的单词计划, 正确加5分, 错误减5分

        asyncRedisUpdateService.updateProficiency(redisTemplate,
                RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix() + userId
                        + RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK.getPrefix() + getLearningWordBookId(userId),
                RedisPrefixEnum.WORD_LEARN_SCHEDULE_LOCK.getPrefix() + userId
                        + RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK_LOCK.getPrefix() + getLearningWordBookId(userId),
                        index,
                        "proficiency",
                        isCorrect? 5 : -5);

    }

    // 每天凌晨0点清空redis中的单词计划
    @Scheduled(cron = "0 0 0 * * ?")
    public void clearWordSchedule() {
        log.info("开始清空redis中的单词计划");
        // 分批清空redis中的单词计划，每批50个
        int batchSize = 50;
        Set<String> keys = redisTemplate.keys(RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix() + "*");
        // 计数器
        int count = 0;
        for (String key : keys) {
            // 从key中拆解用户id和单词书id
            Integer userId = null;
            Integer bookId = null;
            try {
                String[] split = key.split(RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix());
                String[] split1 = split[1].split(RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK.getPrefix());
                userId = Integer.parseInt(split1[0]);
                bookId = Integer.parseInt(split1[1]);
            } catch (NumberFormatException e) {
                log.error("key: {} 解析失败", key);
                continue;
            }

            // 检查userID和bookId是否为空
            if (userId == null || bookId == null){
                continue;
            }

            // 构建map 数据结构为{"user_id": "1", "word_id": "1", "proficiency": "100"}
            List<Map<String, Object>> wordSchedule = new ArrayList<>();
            List<Map<String, Object>> range = redisTemplate.opsForList().range(key, 0, redisTemplate.opsForList().size(key) - 1);
            log.info("redis: {}", JSONUtil.toJsonStr(range));
            Integer finalUserId = userId;
            range.forEach(
                    word -> {
                        Map<String, Object> map = new HashMap<>();
                        map.put("user_id", finalUserId);
                        map.put("word_id", word.get("id"));
                        map.put("proficiency", word.get("proficiency"));
                        wordSchedule.add(map);
                    }
            );
            log.info("wordSchedule: {}", JSONUtil.toJsonStr(wordSchedule));
            // 批量更新mysql
            if (!wordSchedule.isEmpty()) {
                int batchInsertOrUpdate = wordsMapper.batchInsertOrUpdate(wordSchedule);
                log.info("batchInsertOrUpdate: {}", batchInsertOrUpdate);
            }

            // 清空redis中的单词计划
            asyncRedisUpdateService.ClearSchedule(redisTemplate,
                    RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix() + userId + RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK.getPrefix() + bookId,
                    RedisPrefixEnum.WORD_LEARN_SCHEDULE_LOCK.getPrefix() + userId + RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK_LOCK.getPrefix() + bookId);

            count++;
            if (count == batchSize) {
                try {
                    log.info("发送了{}个清理请求，等待5秒再进行下一批", count);
                    // 等待1分钟
                    Thread.sleep(5*1000);
                    count = 0;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        }
    }

    // 每1个小时将redis中的单词计划数据更新到数据库
    @Scheduled(fixedRate = 60 * 60 * 1000)
    public void updateToDatabase() {
        log.info("更新redis的单词计划数据到mysql中");

        // 读取所有redis中的单词计划
        Set<String> keys = redisTemplate.keys(RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix() + "*");
        log.info("keys: {}", JSONUtil.toJsonStr(keys));
        // 遍历所有redis中的单词计划
        for (String key : keys) {
            // 从key中拆解用户id和单词书id
            String[] split = key.split(RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix());
            String[] split1 = split[1].split(RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK.getPrefix());
            Integer userId = Integer.parseInt(split1[0]);
            Integer bookId = Integer.parseInt(split1[1]);
            log.info("userId: {}, bookId: {}", userId, bookId);

            // 构建map 数据结构为{"user_id": "1", "word_id": "1", "proficiency": "100"}
            List<Map<String, Object>> wordSchedule = new ArrayList<>();
            List<Map<String, Object>> range = redisTemplate.opsForList().range(key, 0, redisTemplate.opsForList().size(key) - 1);
            log.info("redis: {}", JSONUtil.toJsonStr(range));
            range.forEach(
                    word -> {
                        Map<String, Object> map = new HashMap<>();
                        map.put("user_id", userId);
                        map.put("word_id", word.get("id"));
                        map.put("proficiency", word.get("proficiency"));
                        wordSchedule.add(map);
                    }
            );
            log.info("wordSchedule: {}", JSONUtil.toJsonStr(wordSchedule));
            // 批量更新mysql
            if (!wordSchedule.isEmpty()) {
                int batchInsertOrUpdate = wordsMapper.batchInsertOrUpdate(wordSchedule);
                log.info("batchInsertOrUpdate: {}", batchInsertOrUpdate);
            }
        }

        log.info("更新完成");
    }

    @Override
    public boolean hasWordsToLearn(int userId) {
        // 获取当前用户正在学习的单词书id
        Integer bookId = getLearningWordBookId(userId);
        if (bookId == null){
            return false;
        }
        // 获取当前单词书内所有可学习的单词的id
        List<Integer> wordIds = getWordIdsByBookIdToLearn(userId, bookId);
        // 检查wordIds是否为空
        if (wordIds == null || wordIds.isEmpty()){
            return false;
        }
        return true;
    }

    @Override
    public int getMaxWordsSize(int userId) {
        // 检查redis是否已经加载了单词计划
        Long size = redisTemplate.opsForList().size(
                RedisPrefixEnum.WORD_LEARN_SCHEDULE.getPrefix() + userId +
                        RedisPrefixEnum.WORD_LEARN_SCHEDULE_BOOK.getPrefix() + getLearningWordBookId(userId)
        );
        if (size == 0){
            return scheduleWords;
        }
        return size.intValue();
    }

    // 从数据库中生成学习计划
    private List<Map<String, Object>> generateWordSchedule(int userId, Integer bookId){
        List<Integer> wordIdsByBookIdToLearn = getWordIdsByBookIdToLearn(userId, bookId);
        if (wordIdsByBookIdToLearn == null || wordIdsByBookIdToLearn.isEmpty()){
            return null;
        }
        // 从wordIds中随机获取20个不重复单词id
        List<Integer> wordIdsToLearn = randomSample(wordIdsByBookIdToLearn, scheduleWords);
        // 使用wordIds获取单词
        List<Map<String, Object>> currentWords = wordsMapper.getWordsWithProficiencyByIds(userId, wordIdsToLearn);
        return currentWords;
    }

    // 获取当前单词书内所有可学习的单词的id
    private List<Integer> getWordIdsByBookIdToLearn(int userId, Integer bookId){
        List<Integer> wordIds = wordsMapper.getWordIdsWithNotFullProficiencyByBookId(userId, bookId);
        return wordsMapper.getWordIdsByBookId(bookId);
    }

    // 获取当前用户正在学习的单词书id
    private Integer getLearningWordBookId(int userId){
        List<WordBook> wordBookList = wordBookService.getLearningWordBook(userId);
        if (wordBookList.isEmpty()){
            return null;
        }
        return wordBookList.get(0).getId();
    }

    // 从列表中随机获取N个不重复元素
    private static <T> List<T> randomSample(List<T> list, int n) {
        // 参数校验
        if (n < 0) {
            return new ArrayList<>();
        }
        // 如果n大于列表大小，则返回整个列表
        if (n > list.size()) {
            return new ArrayList<>(list);
        }

        // 复制原列表以避免修改原数据
        List<T> copy = new ArrayList<>(list);
        // 打乱列表顺序
        Collections.shuffle(copy);
        // 返回前N个元素的新列表
        return new ArrayList<>(copy.subList(0, n));
    }
}
